package com.myk.game.connect6tool.views;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.Xfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;

import com.blankj.utilcode.util.GsonUtils;
import com.myk.game.connect6tool.models.ArrowTipInfo;
import com.myk.game.connect6tool.models.ChessInfo;
import com.myk.game.connect6tool.models.FallTipInfo;
import com.myk.game.connect6tool.models.HistoryInfo;
import com.myk.game.connect6tool.IGameUI;
import com.myk.game.connect6tool.R;
import com.myk.game.connect6tool.data.ActionState;
import com.myk.game.connect6tool.data.SoundData;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class ChessBoardView extends View {

    private Paint mPaint; //动态元素画笔
    private Bitmap bgBuffer; //棋盘背景二级缓冲（界面刷新时不用重新计算和绘制）
    private List<ChessInfo> pieces; //棋盘上的棋子信息集合
    private FallTipInfo fallTip; //最新落子提示信息
    private List<ArrowTipInfo> arrowTipList; //移动箭头提示信息
    private ChessInfo tempPiece; //记录选择的棋子信息
    private Bitmap blackPiece, whitePiece; //棋子图片
    private Bitmap symmetryTip; //成型棋子提示图片
    private Xfermode fallTipXfermode;//绘制落子提示的混合模式
    private int col, row; //棋盘的坐标大小
    private float firstX, firstY; //棋盘的绘制原点，隐式的保存了绘制的偏移信息
    private float placeSize; //棋盘交叉点的间隔距离
    private IGameUI iGameUI; //对游戏外部UI的引用
    private int maxScore; //获胜需要的最大分数
    private String blackName, whiteName; //当前双方名称
    private int blackScore, whiteScore; //当前得分
    private int round; //游戏逻辑回合数（不要根据historyList的长度来计算回合）
    private ActionState actionState; //当前行动状态
    private List<String> historyList; //历史记录列表
    private List<String> turnHistoryList; //反历史记录列表
    private float dX, dY, uX, uY;   //触摸事件按下抬起时的点
    private long dT, uT; //触摸事件按下抬起的时间

    public ChessBoardView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (bgBuffer != null && pieces != null) {
            mPaint.setColor(Color.BLACK);
            canvas.drawBitmap(bgBuffer, 0, 0, mPaint);
            for (ChessInfo piece : pieces) {
                canvas.drawBitmap(piece.isBlack ? blackPiece : whitePiece, piece.startX, piece.startY, mPaint);
                if (piece.isMark) {
                    canvas.drawBitmap(symmetryTip, piece.startX, piece.startY, mPaint);
                }
            }
            if (tempPiece != null) {
                drawFly(canvas);
            }
            if (fallTip != null) {
                drawFocus(canvas);
            }
            if (arrowTipList != null && arrowTipList.size() > 0) {
                drawArrow(canvas);
            }
        }
    }

    //绘制分飞棋子
    private void drawFly(Canvas canvas) {
        mPaint.setColor(0x8000EA90);
        canvas.drawBitmap(tempPiece.isBlack ? blackPiece : whitePiece, tempPiece.startX, tempPiece.startY, mPaint);
        if (matchState(ActionState.BLACK_WAIT_CMX, ActionState.WHITE_WAIT_CMX,
                ActionState.BLACK_WAIT_CM, ActionState.WHITE_WAIT_CM,
                ActionState.BLACK_WAIT_CX, ActionState.WHITE_WAIT_CX,
                ActionState.BLACK_WAIT_MX, ActionState.WHITE_WAIT_MX)) {
            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setStrokeWidth(placeSize / 10.5f);
            float radius = placeSize / 4.5f;
            for (int i = 0; i < col; i++) {
                for (int j = 0; j < row; j++) {
                    if (getChessByPoint(i, j) != null) continue;
                    if (matchState(ActionState.BLACK_WAIT_CMX, ActionState.WHITE_WAIT_CMX,
                            ActionState.BLACK_WAIT_CM, ActionState.WHITE_WAIT_CM,
                            ActionState.BLACK_WAIT_CX, ActionState.WHITE_WAIT_CX) && canChe(i, j) && matchChe(i, j, tempPiece.col, tempPiece.row)) {
                        canvas.drawCircle(firstX + i * placeSize, firstY + j * placeSize, radius, mPaint);
                        continue;
                    }
                    if (matchState(ActionState.BLACK_WAIT_CMX, ActionState.WHITE_WAIT_CMX,
                            ActionState.BLACK_WAIT_CM, ActionState.WHITE_WAIT_CM,
                            ActionState.BLACK_WAIT_MX, ActionState.WHITE_WAIT_MX) && canMa(i, j) && matchMa(i, j, tempPiece.col, tempPiece.row)) {
                        canvas.drawCircle(firstX + i * placeSize, firstY + j * placeSize, radius, mPaint);
                        continue;
                    }
                    if (matchState(ActionState.BLACK_WAIT_CMX, ActionState.WHITE_WAIT_CMX,
                            ActionState.BLACK_WAIT_CX, ActionState.WHITE_WAIT_CX,
                            ActionState.BLACK_WAIT_MX, ActionState.WHITE_WAIT_MX) && canXiang(i, j) && matchXiang(i, j, tempPiece.col, tempPiece.row)) {
                        canvas.drawCircle(firstX + i * placeSize, firstY + j * placeSize, radius, mPaint);
                        continue;
                    }
                }
            }
        }
    }

    //绘制聚焦
    private void drawFocus(Canvas canvas) {
        int saveCount = canvas.saveLayer(0, 0, getWidth(), getHeight(), mPaint);
        mPaint.setColor(0x99CC0000);
        mPaint.setStrokeWidth(placeSize / 12f);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawRect(fallTip.startX, fallTip.startY, fallTip.endX, fallTip.endY, mPaint);
        mPaint.setXfermode(fallTipXfermode);
        canvas.drawCircle(fallTip.enterX, fallTip.enterY, fallTip.radius, mPaint);
        mPaint.setColor(0xFF000000);
        mPaint.setXfermode(null);
        canvas.restoreToCount(saveCount);
    }

    //绘制箭头
    private void drawArrow(Canvas canvas) {
        if (matchState(ActionState.BLACK_ENFORCE, ActionState.WHITE_ENFORCE, ActionState.CONTINUE_DELETE, ActionState.BLACK_CHOOSE_FIRST, ActionState.WHITE_CHOOSE_FIRST, ActionState.WHITE_CHOOSE_SECOND)) {
            return;
        }
        mPaint.setColor(0x99CC0000);
        mPaint.setStrokeWidth(placeSize / 8f);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeCap(Paint.Cap.BUTT);
        for (ArrowTipInfo arrowTip : arrowTipList) {
            canvas.drawLine(arrowTip.startX, arrowTip.startY, arrowTip.endX, arrowTip.endY, mPaint);
            drawTriangle(canvas, arrowTip.startX, arrowTip.startY, arrowTip.endX, arrowTip.endY, placeSize / 2f, placeSize / 3f);
        }
        mPaint.setColor(0xFF000000);
    }

    //绘制线条箭头
    private void drawTriangle(Canvas canvas, float fromX, float fromY, float toX, float toY, float height, float bottom) {
        float juli = (float) Math.sqrt((toX - fromX) * (toX - fromX) + (toY - fromY) * (toY - fromY));// 获取线段距离
        float juliX = toX - fromX;// 有正负，不要取绝对值
        float juliY = toY - fromY;// 有正负，不要取绝对值
        float dianX = toX - (height / juli * juliX);
        float dianY = toY - (height / juli * juliY);
        Path path = new Path(); //终点的箭头
        path.moveTo(toX, toY);// 此点为三边形的起点
        path.lineTo(dianX + (bottom / juli * juliY), dianY - (bottom / juli * juliX));
        path.lineTo(dianX - (bottom / juli * juliY), dianY + (bottom / juli * juliX));
        path.close(); // 使这些点构成封闭的三边形
        canvas.drawPath(path, mPaint);
    }

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            dX = event.getX();
            dY = event.getY();
            dT = System.currentTimeMillis();
        }
        else if (event.getAction() == MotionEvent.ACTION_UP) {
            uX = event.getX();
            uY = event.getY();
            uT = System.currentTimeMillis();
            handleEvent();
        }
        return true;
    }

    /**
     * 设置
     */
    public void setName(String name, boolean isBlack) {
        if (name.length() > 0) {
            if (name.length() > 5) {
                name = name.substring(0, 5);
            }
            if (isBlack) {
                blackName = name;
            }
            else {
                whiteName = name;
            }
            addHistory();
            refreshUI();
        }
    }

    /**
     * 加分操作
     */
    public void addScore(boolean isBlack) {
        if (isBlack) {
            if (blackScore == maxScore) {
                blackScore = 0;
                iGameUI.printlnMsg("黑方得分置0");
            }
            else {
                blackScore++;
                iGameUI.printlnMsg("黑方得分加1");
            }
        }
        else {
            if (whiteScore == maxScore) {
                whiteScore = 0;
                iGameUI.printlnMsg("白方得分置0");
            }
            else {
                whiteScore++;
                iGameUI.printlnMsg("白方得分加1");
            }
        }
        addHistory();
        refreshUI();
    }

    /**
     * 减少回合数操作
     */
    public void reduceRound() {
        if (this.round > 0){
            this.round--;
            addHistory();
            refreshUI();
        }
    }

    /**
     * 切换行动方
     */
    public void changeSide(boolean changeToBlack) {
        if (matchState(ActionState.BLACK_ENFORCE)) {
            changeState(ActionState.WHITE_ENFORCE);
            iGameUI.toast("切换到白方持续落子");
        }
        else if (matchState(ActionState.WHITE_ENFORCE)) {
            changeState(ActionState.BLACK_ENFORCE);
            iGameUI.toast("切换到黑方持续落子");
        }
        else if (matchState(ActionState.CONTINUE_DELETE)) {
            iGameUI.playSound(SoundData.Type.ERROR);
        }
        else {
            if (matchState(ActionState.BLACK_CHOOSE_FIRST)) {
                if (!changeToBlack) {
                    iGameUI.toast("不能切换，黑棋需落下第一颗棋子");
                    iGameUI.playSound(SoundData.Type.ERROR);
                }
            }
            else if (matchState(ActionState.WHITE_CHOOSE_FIRST)) {
                if (changeToBlack) {
                    iGameUI.printlnMsg("不能切换，白方需落下第一颗棋子");
                    iGameUI.playSound(SoundData.Type.ERROR);
                }
            }
            else if (matchState(ActionState.BLACK_CHOOSE_FLY)) {
                iGameUI.printlnMsg("不能切换，请让黑方继续选择飞子落点");
            }
            else if (matchState(ActionState.WHITE_CHOOSE_FLY)) {
                iGameUI.printlnMsg("不能切换，请让白方继续选择飞子落点");
            }
            else if (matchState(ActionState.BLACK_WAIT_CMX, ActionState.BLACK_WAIT_CM, ActionState.BLACK_WAIT_CX, ActionState.BLACK_WAIT_MX)) {
                iGameUI.printlnMsg("不能切换，请让黑方继续选择分路落点");
            }
            else if (matchState(ActionState.WHITE_WAIT_CMX, ActionState.WHITE_WAIT_CM, ActionState.WHITE_WAIT_CX, ActionState.WHITE_WAIT_MX)) {
                iGameUI.printlnMsg("不能切换，请让白方继续选择分路落点");
            }
            else {
                if (changeToBlack) {
                    changeState(ActionState.BLACK_SELECT_DIVIDE);
                    iGameUI.printlnMsg("切换到黑方选择分飞棋子");
                }
                else {
                    changeState(ActionState.WHITE_SELECT_DIVIDE);
                    iGameUI.printlnMsg("切换到白方选择分飞棋子");
                }
            }
        }
        invalidate();
    }

    /**
     * 切换到自动转换模式
     */
    public void autoExchange() {
        int blackCount = 0;
        int whiteCount = 0;
        for (ChessInfo piece : pieces) {
            if (piece.isBlack) {
                blackCount++;
            }
            else {
                whiteCount++;
            }
        }
        if (matchState(ActionState.BLACK_ENFORCE, ActionState.WHITE_ENFORCE, ActionState.CONTINUE_DELETE)) {
            if (blackCount < whiteCount) {
                if (blackCount == 0) {
                    changeState(ActionState.BLACK_CHOOSE_FIRST);
                    iGameUI.printlnMsg("切换到黑方落子");
                }
                else {
                    changeState(ActionState.BLACK_SELECT_DIVIDE);
                    iGameUI.printlnMsg("切换到黑方移动");
                }
            }
            else if (whiteCount < blackCount) {
                if (whiteCount == 0) {
                    changeState(ActionState.WHITE_CHOOSE_FIRST);
                    iGameUI.printlnMsg("切换到白方落子");
                }
                else {
                    changeState(ActionState.WHITE_SELECT_DIVIDE);
                    iGameUI.printlnMsg("切换到白方移动");
                }
            }
            else { //让白方落一子后切换到黑方移动，尽量保持平衡
                changeState(ActionState.WHITE_CHOOSE_SECOND);
                iGameUI.printlnMsg("切换到白方落子");
            }
        }
        else {
            //无变化
        }
        invalidate();
    }

    /**
     * 切换到强制持续模式
     */
    public void forceContinue() {
        if (matchState(ActionState.BLACK_CHOOSE_FIRST)) {
            changeState(ActionState.BLACK_ENFORCE);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        else if (matchState(ActionState.WHITE_CHOOSE_FIRST)) {
            changeState(ActionState.WHITE_ENFORCE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (matchState(ActionState.WHITE_CHOOSE_SECOND)) {
            changeState(ActionState.WHITE_ENFORCE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (matchState(ActionState.BLACK_SELECT_DIVIDE)) {
            changeState(ActionState.BLACK_ENFORCE);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        else if (matchState(ActionState.WHITE_SELECT_DIVIDE)) {
            changeState(ActionState.WHITE_ENFORCE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (matchState(ActionState.BLACK_WAIT_CMX)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_WAIT_CM)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_WAIT_CX)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_WAIT_MX)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_CHOOSE_FLY)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_CMX)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_CM)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_CX)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_MX)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_CHOOSE_FLY)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.BLACK_ENFORCE)) {
            changeState(ActionState.WHITE_ENFORCE);
            iGameUI.printlnMsg("切换到白方持续落子");
        }
        else if (matchState(ActionState.WHITE_ENFORCE)) {
            changeState(ActionState.BLACK_ENFORCE);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        else if (matchState(ActionState.CONTINUE_DELETE)) {
            changeState(ActionState.BLACK_ENFORCE);
            iGameUI.printlnMsg("切换到黑方持续落子");
        }
        invalidate();
    }

    /**
     * 切换到删除模式
     */
    public void deleteMode() {
        if (matchState(ActionState.BLACK_CHOOSE_FIRST)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.WHITE_CHOOSE_FIRST)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.WHITE_CHOOSE_SECOND)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.BLACK_SELECT_DIVIDE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.WHITE_SELECT_DIVIDE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.BLACK_WAIT_CMX)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_WAIT_CM)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_WAIT_CX)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_WAIT_MX)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.BLACK_CHOOSE_FLY)) {
            iGameUI.toast("不能切换，请先让黑棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_CMX)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_CM)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_CX)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_WAIT_MX)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.WHITE_CHOOSE_FLY)) {
            iGameUI.toast("不能切换，请先让白棋完成移动");
        }
        else if (matchState(ActionState.BLACK_ENFORCE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.WHITE_ENFORCE)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.BLACK_WIN)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        else if (matchState(ActionState.WHITE_WIN)) {
            changeState(ActionState.CONTINUE_DELETE);
            iGameUI.printlnMsg("切换到持续提子");
        }
        invalidate();
    }

    /**
     * 初始化棋盘
     */
    public void initChessBoard(IGameUI iGameUI, int col, int row, int maxScore, String blackName, String whiteName) {
        post(() -> {
            this.iGameUI = iGameUI;
            this.col = col;
            this.row = row;
            this.maxScore = maxScore;
            calculateParameter();
            initPaint();
            drawBgBuffer();
            this.pieces = new ArrayList<>();
            this.fallTip = null;
            this.arrowTipList = null;
            this.blackPiece = getBitmap(R.mipmap.ic_black, placeSize, placeSize);
            this.whitePiece = getBitmap(R.mipmap.ic_white, placeSize, placeSize);
            this.symmetryTip = getBitmap(R.mipmap.ic_star4, placeSize, placeSize);
            this.fallTipXfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
            this.maxScore = maxScore;
            this.blackName = blackName;
            this.whiteName = whiteName;
            this.blackScore = 0;
            this.whiteScore = 0;
            this.round = 0;
            this.historyList = new ArrayList<>();
            this.turnHistoryList = new ArrayList<>();
            changeState(ActionState.BLACK_CHOOSE_FIRST); //默认黑棋先落第一个棋子
            addHistory(); //开局保存到历史记录里
            iGameUI.clearMsg();
            iGameUI.printlnMsg("对局开始：黑方先行，先得" + maxScore + "分获胜");
            invalidate();
        });
    }

    /**
     * 从外部来源还原历史记录
     */
    public void restoreExternalHistory(IGameUI iGameUI, HistoryInfo historyInfo) {
        this.iGameUI = iGameUI;
        this.col = historyInfo.getCol();
        this.row = historyInfo.getRow();
        this.maxScore = historyInfo.getMaxScore();
        this.historyList = new ArrayList<>();
        this.turnHistoryList = new ArrayList<>();
        this.fallTip = null;
        this.arrowTipList = null;
        this.tempPiece = historyInfo.getTempPiece();
        this.blackName = historyInfo.getBlackName();
        this.whiteName = historyInfo.getWhiteName();
        this.whiteScore = historyInfo.getWhiteScore();
        this.blackScore = historyInfo.getBlackScore();
        this.round = historyInfo.getRound();
        this.actionState = historyInfo.getActionState();
        calculateParameter();
        drawBgBuffer();
        this.blackPiece = getBitmap(R.mipmap.ic_black, placeSize, placeSize);
        this.whitePiece = getBitmap(R.mipmap.ic_white, placeSize, placeSize);
        this.symmetryTip = getBitmap(R.mipmap.ic_star4, placeSize, placeSize);
        this.pieces = new ArrayList<>();
        List<ChessInfo> chessInfoList = historyInfo.getPieces();
        for (ChessInfo chessInfo : chessInfoList) {
            fallPiece(chessInfo.isBlack, chessInfo.col, chessInfo.row);
        }
        FallTipInfo fallTipInfo = historyInfo.getFallTip();
        if (fallTipInfo != null) {
            setFallTip(fallTipInfo.pCol, fallTipInfo.pRow);
        }
        List<ArrowTipInfo> arrowTipList = historyInfo.getArrowTipList();
        if (tempPiece != null && arrowTipList != null) {
            for (ArrowTipInfo arrowTipInfo : arrowTipList) {
                setArrowTip(arrowTipInfo.pCol, arrowTipInfo.pRow);
            }
        }
        addHistory();
        refreshUI();
        invalidate();
    }

    /**
     * 返回历史记录
     */
    public List<String> getHistoryList() {
        return historyList;
    }

    /**
     * 返回上一步
     */
    public void undoOnce() {
        if (historyList.size() <= 1) {
            iGameUI.toast("已在最初回合");
        }
        else {
            String curHistory = historyList.get(historyList.size() - 1);
            String lastHistory = historyList.get(historyList.size() - 2);
            historyList.remove(curHistory);
            turnHistoryList.add(curHistory);
            HistoryInfo lastHistoryInfo = GsonUtils.fromJson(lastHistory, HistoryInfo.class);
            restoreInternalHistory(lastHistoryInfo);
        }
    }

    /**
     * 还原下一步
     */
    public void redoOnce() {
        if (turnHistoryList.size() > 0) {
            String laterHistory = turnHistoryList.get(turnHistoryList.size() - 1);
            turnHistoryList.remove(laterHistory);
            historyList.add(laterHistory);
            HistoryInfo laterHistoryInfo = GsonUtils.fromJson(laterHistory, HistoryInfo.class);
            restoreInternalHistory(laterHistoryInfo);
        }
        else {
            iGameUI.toast("已在最新回合");
        }
    }

    /**
     * 设置历史纪录
     */
    public void setHistoryList(List<String> historyList) {
        this.historyList = historyList;
    }

    //把当前状态和数据添加到历史记录中
    private void addHistory() {
        HistoryInfo historyInfo = new HistoryInfo();
        historyInfo.setPieces(pieces);
        historyInfo.setFallTip(fallTip);
        historyInfo.setArrowTipList(arrowTipList);
        historyInfo.setTempPiece(tempPiece);
        historyInfo.setMaxScore(maxScore);
        historyInfo.setBlackName(blackName);
        historyInfo.setWhiteName(whiteName);
        historyInfo.setBlackScore(blackScore);
        historyInfo.setWhiteScore(whiteScore);
        historyInfo.setRound(round);
        historyInfo.setCol(col);
        historyInfo.setRow(row);
        historyInfo.setActionState(actionState);
        historyList.add(GsonUtils.toJson(historyInfo)); //添加历史记录
        turnHistoryList.clear(); //走了新步这反历史就没了
    }

    //还原局内历史记录
    private void restoreInternalHistory(HistoryInfo historyInfo) {
        pieces = historyInfo.getPieces();
        fallTip = historyInfo.getFallTip();
        arrowTipList = historyInfo.getArrowTipList();
        tempPiece = historyInfo.getTempPiece();
        maxScore = historyInfo.getMaxScore();
        blackName = historyInfo.getBlackName();
        whiteName = historyInfo.getWhiteName();
        whiteScore = historyInfo.getWhiteScore();
        blackScore = historyInfo.getBlackScore();
        round = historyInfo.getRound();
        col = historyInfo.getCol();
        row = historyInfo.getRow();
        actionState = historyInfo.getActionState();
        refreshUI();
        invalidate();
    }

    //计算绘制参数
    private void calculateParameter() {
        float width = getWidth();
        float height = getHeight();
        float minS = Math.min(width, height);
        int maxL = Math.max(col, row);
        this.placeSize = minS / (maxL + 1); //加1表示最小预留一个格子的边距
        this.firstX = (width - (col - 1) * placeSize) / 2;
        this.firstY = (height - (row - 1) * placeSize) / 2;
    }

    //初始化画笔
    private void initPaint() {
        this.mPaint = new Paint();
        this.mPaint.setAntiAlias(true);
        this.mPaint.setDither(true);
        this.mPaint.setFilterBitmap(true);
    }

    //初始绘制二级缓冲背景
    private void drawBgBuffer() {
        this.bgBuffer = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
        Canvas buffCanvas = new Canvas(bgBuffer);
        //画带光晕的背景色
        float gX = firstX + placeSize * col / 2;
        float gY = firstY + placeSize * row / 2;
        float gR = placeSize * col / 2;
        mPaint.setShader(new RadialGradient(gX, gY, gR, 0xFFEEBE89, 0xFFEBA555, Shader.TileMode.CLAMP));
        buffCanvas.drawPaint(mPaint);
        mPaint.setShader(null);
        mPaint.setStrokeWidth(placeSize / 24f);
        mPaint.setStrokeJoin(Paint.Join.ROUND);
        int textSize = (int) (placeSize / 2f);
        mPaint.setTextSize(textSize);
        mPaint.setFakeBoldText(true);
        mPaint.setTextAlign(Paint.Align.CENTER);// 文字原点变为了中下点
        //画线和坐标
        for (int i = 0; i < col; i++) {
            float sX = firstX + i * placeSize;
            float sY = firstY;
            float eX = sX;
            float eY = firstY + (row - 1) * placeSize;
            mPaint.setColor(Color.DKGRAY);
            buffCanvas.drawLine(sX, sY, eX, eY, mPaint);
            float tX = sX;
            float ty = (sY + textSize) / 2 - sY / 8;
            mPaint.setColor(Color.DKGRAY);
            buffCanvas.drawText(String.valueOf((char) ('A' + i)), tX, ty, mPaint);
        }
        for (int i = 0; i < row; i++) {
            float sX = firstX;
            float sY = firstY + i * placeSize;
            float eX = firstX + (col - 1) * placeSize;
            float eY = sY;
            mPaint.setColor(Color.GRAY);
            buffCanvas.drawLine(sX, sY, eX, eY, mPaint);
            float tX = sX * 0.4f;
            float ty = sY + textSize / 2f;
            mPaint.setColor(Color.BLACK);
            buffCanvas.drawText(String.valueOf(row - i), tX, ty, mPaint);
        }
        //保证尽量均分的画点
        mPaint.setColor(Color.BLACK);
        mPaint.setStrokeWidth(placeSize / 8f);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        int centerCol = col / 2;
        int centerRow = row / 2;
        if ((col & 1) == 1 && (row & 1) == 1) {
            buffCanvas.drawPoint(firstX + centerCol * placeSize, firstY + centerRow * placeSize, mPaint);
        }
        int p1C = centerCol / 2;
        int p1R = centerRow / 2;
        buffCanvas.drawPoint(firstX + p1C * placeSize, firstY + p1R * placeSize, mPaint);
        int p2C = col - 1 - p1C;
        int p2R = p1R;
        buffCanvas.drawPoint(firstX + p2C * placeSize, firstY + p2R * placeSize, mPaint);
        int p3C = p1C;
        int p3R = row - 1 - p1R;
        buffCanvas.drawPoint(firstX + p3C * placeSize, firstY + p3R * placeSize, mPaint);
        int p4C = p2C;
        int p4R = p3R;
        buffCanvas.drawPoint(firstX + p4C * placeSize, firstY + p4R * placeSize, mPaint);
    }

    //刷新游戏UI
    private void refreshUI() {
        iGameUI.refreshUI(actionState, blackName, whiteName, blackScore, whiteScore,round);
    }

    //处理触摸事件
    private void handleEvent() {
        if (getDistance(dX, dY, uX, uY) > placeSize) {
            return; //移动距离超过一格视为放弃落子
        }
        if ((uT - dT) < 50) {
            return; //触摸时间过短视为误触
        }
        if (checkEventRange()) {
            int[] coordinate = getCoordinateByPoint();
            int pCol = coordinate[0];
            int pRow = coordinate[1];
            processFallAction(pCol, pRow);
        }
    }

    //处理落子行为
    private void processFallAction(int pCol, int pRow) {
        //根据坐标获取该位置的信息，为null就是空位置，不为空就是已有棋子了
        ChessInfo chessInfo = getChessByPoint(pCol, pRow);
        //等待黑棋落下第一颗棋子
        if (matchState(ActionState.BLACK_CHOOSE_FIRST)) {
            fallPiece(true, pCol, pRow); //落下黑棋子
            setFallTip(pCol, pRow); //添加落子提示
            clearArrowTip();//清除箭头提示
            round++; //添加回合数
            iGameUI.playSound(SoundData.Type.FALL); //播放声音
            changeState(ActionState.WHITE_CHOOSE_FIRST); //切换白子落子
            addHistory();//添加到历史记录
        }
        //等待白棋落下第一颗棋子
        else if (matchState(ActionState.WHITE_CHOOSE_FIRST)) {
            if (chessInfo == null) {
                fallPiece(false, pCol, pRow); //落下黑棋子
                setFallTip(pCol, pRow); //添加落子提示
                clearArrowTip();//清除箭头提示
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                changeState(ActionState.WHITE_CHOOSE_SECOND); //切换白子落子
                addHistory();//添加到历史记录
            }
            else {
                iGameUI.toast("白棋请落在空白位置"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待白棋落下第二颗棋子
        else if (matchState(ActionState.WHITE_CHOOSE_SECOND)) {
            if (chessInfo == null) {
                fallPiece(false, pCol, pRow); //落下白棋子
                setFallTip(pCol, pRow); //添加落子提示
                clearArrowTip();//清除箭头提示
                round++; //添加回合数
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                changeState(ActionState.BLACK_SELECT_DIVIDE); //切换黑棋移动
                addHistory();//添加到历史记录
            }
            else {
                iGameUI.toast("白棋请落在空白位置"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待黑棋选择分飞棋子
        else if (matchState(ActionState.BLACK_SELECT_DIVIDE)) {
            if (chessInfo != null && chessInfo.isBlack) {
                int canCount = 0;
                if (canChe(pCol, pRow)) canCount++;
                if (canMa(pCol, pRow)) canCount++;
                if (canXiang(pCol, pRow)) canCount++;
                if (canCount < 2) {
                    iGameUI.toast("线路堵塞，黑棋请重新选择分飞棋子"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放提子声音
                }
                else {
                    selectDividePiece(chessInfo); //选择了要分飞的棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    clearArrowTip();//清除箭头提示
                    iGameUI.playSound(SoundData.Type.PICK); //播放声音
                    changeState(ActionState.BLACK_WAIT_CMX); //切换黑棋移动
                    addHistory();//添加到历史记录
                }
            }
            else {
                iGameUI.toast("黑棋请选择分飞棋子"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放提子声音
            }
        }
        //等待白棋选择分飞棋子
        else if (matchState(ActionState.WHITE_SELECT_DIVIDE)) {
            if (chessInfo != null && !chessInfo.isBlack) {
                int canCount = 0;
                if (canChe(pCol, pRow)) canCount++;
                if (canMa(pCol, pRow)) canCount++;
                if (canXiang(pCol, pRow)) canCount++;
                if (canCount < 2) {
                    iGameUI.toast("线路堵塞，白棋请重新选择分飞棋子"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放提子声音
                }
                else {
                    selectDividePiece(chessInfo); //选择了要分飞的棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    clearArrowTip();//清除箭头提示
                    iGameUI.playSound(SoundData.Type.PICK); //播放提子声音
                    changeState(ActionState.WHITE_WAIT_CMX); //切换黑棋移动
                    addHistory();//添加到历史记录
                }
            }
            else {
                iGameUI.toast("白棋请选择分飞棋子"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待黑棋选择第一个分路落点（车马象）
        else if (matchState(ActionState.BLACK_WAIT_CMX)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchChe = matchChe(pCol, pRow, lastCol, lastRow);
                boolean isMatchMa = matchMa(pCol, pRow, lastCol, lastRow);
                boolean isMatchXiang = matchXiang(pCol, pRow, lastCol, lastRow);
                if (isMatchChe || isMatchMa || isMatchXiang) {
                    if (tempPiece.isMark){
                        tempPiece.setMark(false); //分子落下后，移除标记
                        blackScore--;//记得减分
                    }
                    fallPiece(true, pCol, pRow); //落下黑棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    if (isMatchChe) {
                        changeState(ActionState.BLACK_WAIT_MX); //黑棋继续落子
                    }
                    if (isMatchMa) {
                        changeState(ActionState.BLACK_WAIT_CX); //黑棋继续落子
                    }
                    if (isMatchXiang) {
                        changeState(ActionState.BLACK_WAIT_CM); //黑棋继续落子
                    }
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("黑棋选择第一个分路落点（车马象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                if (chessInfo.isBlack){
                    int canCount = 0;
                    if (canChe(pCol, pRow)) canCount++;
                    if (canMa(pCol, pRow)) canCount++;
                    if (canXiang(pCol, pRow)) canCount++;
                    if (canCount < 2) {
                        iGameUI.toast("黑棋选择第一个分路落点（车马象）"); //提示用户不能这么做
                        iGameUI.playSound(SoundData.Type.ERROR); //播放提子声音
                    }
                    else {
                        changeDividePiece(chessInfo); //切换要分飞的棋子
                        setFallTip(pCol, pRow); //添加落子提示
                        clearArrowTip();//清除箭头提示
                        iGameUI.playSound(SoundData.Type.PICK); //播放声音
                        changeState(ActionState.BLACK_WAIT_CMX); //切换黑棋移动
                        addHistory();//添加到历史记录
                    }
                }
                else {
                    iGameUI.toast("黑棋选择第一个分路落点（车马象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
        }
        //等待黑棋选择第二个分路落点（车马）
        else if (matchState(ActionState.BLACK_WAIT_CM)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchChe = matchChe(pCol, pRow, lastCol, lastRow);
                boolean isMatchMa = matchMa(pCol, pRow, lastCol, lastRow);
                if (isMatchChe || isMatchMa) {
                    fallPiece(true, pCol, pRow); //落下黑棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    changeState(ActionState.BLACK_CHOOSE_FLY);//黑棋落子
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("黑棋选择第一个分路落点（车马）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                iGameUI.toast("黑棋选择第一个分路落点（车马）"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待黑棋选择第二个分路落点（车象）
        else if (matchState(ActionState.BLACK_WAIT_CX)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchChe = matchChe(pCol, pRow, lastCol, lastRow);
                boolean isMatchXiang = matchXiang(pCol, pRow, lastCol, lastRow);
                if (isMatchChe || isMatchXiang) {
                    fallPiece(true, pCol, pRow); //落下黑棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //++添加可移动射线提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音、
                    changeState(ActionState.BLACK_CHOOSE_FLY);//黑棋落子
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("黑棋选择第一个分路落点（车象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                iGameUI.toast("黑棋选择第一个分路落点（车象）"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待黑棋选择第二个分路落点（马象）
        else if (matchState(ActionState.BLACK_WAIT_MX)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchMa = matchMa(pCol, pRow, lastCol, lastRow);
                boolean isMatchXiang = matchXiang(pCol, pRow, lastCol, lastRow);
                if (isMatchMa || isMatchXiang) {
                    fallPiece(true, pCol, pRow); //落下黑棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //++添加可移动射线提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    changeState(ActionState.BLACK_CHOOSE_FLY);//黑棋落子
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("黑棋选择第一个分路落点（马象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                iGameUI.toast("黑棋选择第一个分路落点（马象）"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待白棋选择第一个分路落点（车马象）
        else if (matchState(ActionState.WHITE_WAIT_CMX)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchChe = matchChe(pCol, pRow, lastCol, lastRow);
                boolean isMatchMa = matchMa(pCol, pRow, lastCol, lastRow);
                boolean isMatchXiang = matchXiang(pCol, pRow, lastCol, lastRow);
                if (isMatchChe || isMatchMa || isMatchXiang) {
                    if (tempPiece.isMark){
                        tempPiece.setMark(false); //分子落下后，移除标记
                        whiteScore--;//记得减分
                    }
                    fallPiece(false, pCol, pRow); //落下白棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    if (isMatchChe) {
                        changeState(ActionState.WHITE_WAIT_MX); //继续落子
                    }
                    if (isMatchMa) {
                        changeState(ActionState.WHITE_WAIT_CX); //继续落子
                    }
                    if (isMatchXiang) {
                        changeState(ActionState.WHITE_WAIT_CM); //继续落子
                    }
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("白棋选择第一个分路落点（车马象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                if (!chessInfo.isBlack){
                    int canCount = 0;
                    if (canChe(pCol, pRow)) canCount++;
                    if (canMa(pCol, pRow)) canCount++;
                    if (canXiang(pCol, pRow)) canCount++;
                    if (canCount < 2) {
                        iGameUI.toast("白棋选择第一个分路落点（车马象）"); //提示用户不能这么做
                        iGameUI.playSound(SoundData.Type.ERROR); //播放提子声音
                    }
                    else {
                        changeDividePiece(chessInfo); //切换要分飞的棋子
                        setFallTip(pCol, pRow); //添加落子提示
                        clearArrowTip();//清除箭头提示
                        iGameUI.playSound(SoundData.Type.PICK); //播放声音
                        changeState(ActionState.WHITE_WAIT_CMX); //切换黑棋移动
                        addHistory();//添加到历史记录
                    }
                }
                else {
                    iGameUI.toast("白棋选择第一个分路落点（车马象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
        }
        //等待白棋选择第二个分路落点（车马）
        else if (matchState(ActionState.WHITE_WAIT_CM)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchChe = matchChe(pCol, pRow, lastCol, lastRow);
                boolean isMatchMa = matchMa(pCol, pRow, lastCol, lastRow);
                if (isMatchChe || isMatchMa) {
                    fallPiece(false, pCol, pRow); //落下白棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //++添加可移动射线提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    changeState(ActionState.WHITE_CHOOSE_FLY);//白棋落子
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("白棋选择第二个分路落点（车马）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                iGameUI.toast("白棋选择第二个分路落点（车马）"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待白棋选择第二个分路落点（车象）
        else if (matchState(ActionState.WHITE_WAIT_CX)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchChe = matchChe(pCol, pRow, lastCol, lastRow);
                boolean isMatchXiang = matchXiang(pCol, pRow, lastCol, lastRow);
                if (isMatchChe || isMatchXiang) {
                    fallPiece(false, pCol, pRow); //落下白棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //++添加可移动射线提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    changeState(ActionState.WHITE_CHOOSE_FLY);//白棋落子
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("白棋选择第二个分路落点（车象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                iGameUI.toast("白棋选择第二个分路落点（车象）"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待白棋选择第二个分路落点（马象）
        else if (matchState(ActionState.WHITE_WAIT_MX)) {
            if (chessInfo == null) {
                int lastCol = tempPiece.col;
                int lastRow = tempPiece.row;
                boolean isMatchMa = matchMa(pCol, pRow, lastCol, lastRow);
                boolean isMatchXiang = matchXiang(pCol, pRow, lastCol, lastRow);
                if (isMatchMa || isMatchXiang) {
                    fallPiece(false, pCol, pRow); //落下白棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    //++添加可移动射线提示
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    changeState(ActionState.WHITE_CHOOSE_FLY);//白棋落子
                    addHistory();//添加到历史记录
                }
                else {
                    iGameUI.toast("白棋选择第二个分路落点（马象）"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
            }
            else {
                iGameUI.toast("白棋选择第二个分路落点（马象）"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待黑棋选择飞子落点
        else if (matchState(ActionState.BLACK_CHOOSE_FLY)) {
            if (chessInfo == null) {
                if (pCol == tempPiece.col && pRow == tempPiece.row) {
                    iGameUI.toast("黑棋选择飞子落点不能停留原地"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
                else {
                    fallPiece(true, pCol, pRow); //落下黑棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    tempPiece = null; //清除临时棋子
                    round++; //添加回合数
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    List<List<ChessInfo>> sixOrMoreInARowList = getSixOrMoreInARow(pieces, true);
                    if (sixOrMoreInARowList.size() > 0) {
                        markStarAndCountScore(sixOrMoreInARowList); //标星并且算分
                        if (blackScore >= maxScore) {
                            changeState(ActionState.BLACK_WIN);
                        }
                        else {
                            changeState(ActionState.WHITE_SELECT_DIVIDE); //切换白棋行动
                        }
                    }
                    else {
                        changeState(ActionState.WHITE_SELECT_DIVIDE); //切换白棋行动
                    }
                    addHistory();//添加到历史记录
                }
            }
            else {
                iGameUI.toast("黑棋选择飞子落点"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //等待白棋选择飞子落点
        else if (matchState(ActionState.WHITE_CHOOSE_FLY)) {
            if (chessInfo == null) {
                if (pCol == tempPiece.col && pRow == tempPiece.row) {
                    iGameUI.toast("白棋选择飞子落点不能停留原地"); //提示用户不能这么做
                    iGameUI.playSound(SoundData.Type.ERROR); //播放声音
                }
                else {
                    fallPiece(false, pCol, pRow); //落下白棋子
                    setFallTip(pCol, pRow); //添加落子提示
                    setArrowTip(pCol, pRow); //添加箭头提示
                    tempPiece = null; //清除临时棋子
                    round++; //添加回合数
                    iGameUI.playSound(SoundData.Type.FALL); //播放声音
                    List<List<ChessInfo>> sixOrMoreInARowList = getSixOrMoreInARow(pieces, false);
                    if (sixOrMoreInARowList.size() > 0) {
                        markStarAndCountScore(sixOrMoreInARowList); //标星并且算分
                        if (whiteScore >= maxScore) {
                            changeState(ActionState.WHITE_WIN);
                        }
                        else {
                            changeState(ActionState.BLACK_SELECT_DIVIDE); //切换黑棋行动
                        }
                    }
                    else {
                        changeState(ActionState.BLACK_SELECT_DIVIDE); //切换黑棋行动
                    }
                    addHistory();//添加到历史记录
                }
            }
            else {
                iGameUI.toast("白棋选择飞子落点"); //提示用户不能这么做
                iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        //强制持续下黑棋
        else if (matchState(ActionState.BLACK_ENFORCE)) {
            //落子空白处
            if (chessInfo == null) {
                fallPiece(true, pCol, pRow); //落下黑棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
            }
            //落子白棋处
            else if (!chessInfo.isBlack) {
                chessInfo.isBlack = true; //原来的白棋强制变为黑棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            //落在黑棋处
            else {
                pieces.remove(chessInfo); //移除该黑棋
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            setFallTip(pCol, pRow); //添加落子提示
            clearArrowTip(); //移除箭头提示
            addHistory();//添加到历史记录
        }
        //强制持续下白棋
        else if (matchState(ActionState.WHITE_ENFORCE)) {
            //落在空白处
            if (chessInfo == null) {
                fallPiece(false, pCol, pRow); //落下白棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
            }
            //落在白棋处
            else if (chessInfo.isBlack) {
                chessInfo.isBlack = false; //强制黑棋变白棋
                iGameUI.playSound(SoundData.Type.FALL); //播放声音
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            //落在白棋处
            else {
                pieces.remove(chessInfo); //移除该白棋
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
            }
            setFallTip(pCol, pRow); //添加落子提示
            clearArrowTip(); //移除箭头提示
            addHistory();//添加到历史记录
        }
        //等待移除棋子
        else if (matchState(ActionState.CONTINUE_DELETE)) {
            if (chessInfo != null) {
                //symmetryPieces.clear(); //清空成型棋子列表
                pieces.remove(chessInfo); //移除该棋子
                setFallTip(pCol, pRow); //添加落子提示
                clearArrowTip(); //移除箭头提示
                iGameUI.playSound(SoundData.Type.PICK); //播放声音
                addHistory(); //添加到历史记录
            }
            else {
                //iGameUI.toast("只能在有棋子的位置提取"); //提示用户不能这么做
                //iGameUI.playSound(SoundData.Type.ERROR); //播放声音
            }
        }
        invalidate(); //每次触发事件不管有没有变化都通知棋盘重绘
    }

    //切换分飞棋子
    private void changeDividePiece(ChessInfo chessInfo) {
        pieces.add(tempPiece);
        pieces.remove(chessInfo); //移除将要分飞棋子
        tempPiece = chessInfo; //记录分飞棋子
    }

    //选择分飞棋子
    private void selectDividePiece(ChessInfo chessInfo) {
        pieces.remove(chessInfo); //移除将要分飞棋子
        tempPiece = chessInfo; //记录分飞棋子
    }

    //标星并且算分
    private void markStarAndCountScore(List<List<ChessInfo>> sixOrMoreInARowList) {
        boolean isBlack = false;
        for (List<ChessInfo> chessInfos : sixOrMoreInARowList) {
            for (ChessInfo chessInfo : chessInfos) {
                chessInfo.setMark(true);
                isBlack = chessInfo.isBlack;
            }
        }
        int count = 0;
        for (ChessInfo piece : pieces) {
            if (piece.isMark && piece.isBlack == isBlack) {
                count++;
            }
        }
        if (isBlack) {
            this.blackScore = count;
        }
        else {
            this.whiteScore = count;
        }
    }

    //判断象路是否有路可走
    private boolean canXiang(int pCol, int pRow) {
        if (getChessByPoint(pCol - 1, pRow - 1) == null) return true;
        if (getChessByPoint(pCol - 1, pRow + 1) == null) return true;
        if (getChessByPoint(pCol + 1, pRow - 1) == null) return true;
        if (getChessByPoint(pCol + 1, pRow + 1) == null) return true;
        return false;
    }

    //判断马路是否有路可走
    private boolean canMa(int pCol, int pRow) {
        if (getChessByPoint(pCol - 1, pRow - 2) == null) return true;
        if (getChessByPoint(pCol - 1, pRow + 2) == null) return true;
        if (getChessByPoint(pCol + 1, pRow - 2) == null) return true;
        if (getChessByPoint(pCol + 1, pRow + 2) == null) return true;
        if (getChessByPoint(pCol - 2, pRow - 1) == null) return true;
        if (getChessByPoint(pCol - 2, pRow + 1) == null) return true;
        if (getChessByPoint(pCol + 2, pRow - 1) == null) return true;
        if (getChessByPoint(pCol + 2, pRow + 1) == null) return true;
        return false;
    }

    //判断车路是否有路可走
    private boolean canChe(int pCol, int pRow) {
        if (getChessByPoint(pCol - 1, pRow) == null) return true;
        if (getChessByPoint(pCol + 1, pRow) == null) return true;
        if (getChessByPoint(pCol, pRow - 1) == null) return true;
        if (getChessByPoint(pCol, pRow + 1) == null) return true;
        return false;
    }

    //判断移动路线是否为象路
    private boolean matchXiang(int pCol, int pRow, int lastCol, int lastRow) {
        if (Math.abs(pCol - lastCol) == Math.abs(pRow - lastRow)) { //落点和分飞点是斜线
            for (ChessInfo piece : pieces) {
                if (Math.abs(pCol - piece.col) == Math.abs(pRow - piece.row)) { //棋子和落点是斜线
                    if (piece.col > Math.min(pCol, lastCol) && piece.col < Math.max(pCol, lastCol)) {
                        if (piece.row > Math.min(pRow, lastRow) && piece.row < Math.max(pRow, lastRow)) {
                            return false;//棋子在落点和分飞点之间
                        }
                    }
                }
            }
            return true;
        }
        return false;
    }

    //判断移动路线是否为马路
    private boolean matchMa(int pCol, int pRow, int lastCol, int lastRow) {
        if (Math.abs(pCol - lastCol) == 1 && Math.abs(pRow - lastRow) == 2) {
            return true;
        }
        if (Math.abs(pRow - lastRow) == 1 && Math.abs(pCol - lastCol) == 2) {
            return true;
        }
        return false;
    }

    //判断移动路线是否为车路
    private boolean matchChe(int pCol, int pRow, int lastCol, int lastRow) {
        if (Math.abs(pCol - lastCol) == 0 || Math.abs(pRow - lastRow) == 0) {
            for (ChessInfo piece : pieces) {
                if (Math.abs(pCol - piece.col) == 0) {
                    if (piece.row > Math.min(pRow, lastRow) && piece.row < Math.max(pRow, lastRow)) {
                        return false;//棋子在落点和分飞点之间
                    }
                }
                if (Math.abs(pRow - piece.row) == 0) {
                    if (piece.col > Math.min(pCol, lastCol) && piece.col < Math.max(pCol, lastCol)) {
                        return false;//棋子在落点和分飞点之间
                    }
                }
            }
            return true;
        }
        return false;
    }

    //判断当前状态 (可多选，满足一项就返回true)
    private boolean matchState(ActionState... state) {
        for (ActionState sta : state) {
            if (actionState == sta) {
                return true;
            }
        }
        return false;
    }

    //转换状态
    private void changeState(ActionState state) {
        actionState = state;
        refreshUI();
    }

    //在棋盘上指定坐标上落下新棋子
    private void fallPiece(boolean isBlack, int pCol, int pRow) {
        float enterX = firstX + pCol * placeSize; //坐标对应的交叉点的x坐标
        float enterY = firstY + pRow * placeSize;//坐标对应的交叉点的y坐标
        float halfSize = placeSize / 2; //格子距离的一半，用于计算
        ChessInfo p = new ChessInfo(isBlack, pCol, pRow, enterX - halfSize, enterY - halfSize); //创建新的黑棋
        pieces.add(p); //落下黑子
    }

    //在指定坐标上添加落子提示
    private void setFallTip(int pCol, int pRow) {
        float enterX = firstX + pCol * placeSize; //坐标对应的交叉点的x坐标
        float enterY = firstY + pRow * placeSize;//坐标对应的交叉点的y坐标
        float halfSize = placeSize / 2; //格子距离的一半，用于计算
        float offsetSize = halfSize * 0.85f;
        float radiusSize = halfSize;
        fallTip = new FallTipInfo(pCol, pRow, enterX - offsetSize, enterY - offsetSize, enterX + offsetSize, enterY + offsetSize, enterX, enterY, radiusSize);//增加落子提示图标
    }

    //指定坐标设定箭头提示
    private void setArrowTip(int pCol, int pRow) {
        if (arrowTipList == null) arrowTipList = new ArrayList<>();
        float startX = firstX + tempPiece.col * placeSize;
        float startY = firstY + tempPiece.row * placeSize;
        float endX = firstX + pCol * placeSize;
        float endY = firstY + pRow * placeSize;
        ArrowTipInfo arrowTip = new ArrowTipInfo(pCol, pRow, startX, startY, endX, endY);
        arrowTipList.add(arrowTip);
    }

    //清除箭头提示
    private void clearArrowTip() {
        if (arrowTipList != null) arrowTipList.clear();
    }

    //根据坐标点获取棋子
    private ChessInfo getChessByPoint(int pCol, int pRow) {
        if (tempPiece != null && tempPiece.col == pCol && tempPiece.row == pRow) return tempPiece;
        for (ChessInfo piece : pieces) {
            if (piece.col == pCol && piece.row == pRow) {
                return piece;
            }
        }
        return null;
    }

    //判断点击事件是否在有效范围内
    private boolean checkEventRange() {
        float halfSize = placeSize / 2;
        float minX = firstX - halfSize;
        float maxX = firstX + col * placeSize - halfSize;
        float minY = firstY - halfSize;
        float maxY = firstY + row * placeSize - halfSize;
        return uX > minX && uX < maxX && uY > minY && uY < maxY;
    }

    //根据点击事件获取坐标
    private int[] getCoordinateByPoint() {
        int[] c = new int[2];
        int x = (int) ((uX - firstX + placeSize / 2) / placeSize);
        int y = (int) ((uY - firstY + placeSize / 2) / placeSize);
        c[0] = x;
        c[1] = y;
        return c;
    }

    //获取所有连珠棋子集合
    public static List<List<ChessInfo>> getSixOrMoreInARow(List<ChessInfo> pieces, boolean isBlack) {
        List<List<ChessInfo>> result = new ArrayList<>();
        Set<ChessInfo> processedPieces = new HashSet<>(); // 用于存储已处理的棋子

        // 定义方向数组，分别代表水平、垂直、主对角线和副对角线四个方向
        final int[][] DIRECTIONS = {
                {0, 1},   // 水平向右
                {1, 0},   // 垂直向下
                {1, 1},   // 主对角线向右下
                {1, -1}   // 副对角线向右上
        };
        for (ChessInfo start : pieces) {
            // 只处理与给定颜色匹配且未被处理过的棋子
            if (start.isBlack != isBlack || start.isMark || !processedPieces.add(start)) continue;

            for (int[] direction : DIRECTIONS) {
                List<ChessInfo> sequence = new ArrayList<>();
                sequence.add(start);
                // 向一个方向探索
                ChessInfo current = getNextInLine(pieces, start, direction, true);
                boolean found = false;
                while (current != null) {
                    sequence.add(current);
                    if (sequence.size() >= 6) {
                        found = true;
                    }
                    current = getNextInLine(pieces, current, direction, true);
                }
                // 向相反方向探索
                current = getNextInLine(pieces, start, direction, false);
                found = false;
                while (current != null) {
                    sequence.add(0, current); // 在序列开始处添加
                    if (sequence.size() >= 6) {
                        found = true;
                    }
                    current = getNextInLine(pieces, current, direction, false);
                }
                if (found) {
                    result.add(new ArrayList<>(sequence));
                    processedPieces.addAll(sequence);
                }
            }
        }

        return result;
    }

    private static ChessInfo getNextInLine(List<ChessInfo> pieces, ChessInfo from, int[] direction, boolean forward) {
        int nextCol = from.col + (forward ? direction[1] : -direction[1]);
        int nextRow = from.row + (forward ? direction[0] : -direction[0]);

        for (ChessInfo piece : pieces) {
            if (piece.col == nextCol && piece.row == nextRow && piece.isBlack == from.isBlack && !piece.isMark) {
                return piece;
            }
        }

        return null;
    }

    //获取两点之间的距离
    private float getDistance(float x0, float y0, float x1, float y1) {
        float a = (y0 - y1) * (y0 - y1) + (x0 - x1) * (x0 - x1);
        return (float) Math.sqrt(a);
    }

    //判断两个棋子是否相邻(直向和斜向相邻都算,位置相同不算)
    private boolean isAdjacent(ChessInfo a, ChessInfo b) {
        if (a.col == b.col && a.row == b.row) {
            return false;
        }
        else {
            return Math.abs(a.col - b.col) <= 1 && Math.abs(a.row - b.row) <= 1;
        }
    }

    //获取指定大小的图片
    private Bitmap getBitmap(@DrawableRes int sourceId, float width, float height) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), sourceId);
        if (bitmap.getWidth() == width && bitmap.getHeight() == height) {    //写这个判断，避createScaledBitmap()的坑
            return bitmap;
        }
        return Bitmap.createScaledBitmap(bitmap, (int) width, (int) height, true);
    }

}
